Skip to content

feat: add a keyboard shortcuts help screen - #294

Merged
stabldev merged 5 commits into
stabldev:mainfrom
YousefHadder:feat/help-screen
Aug 18, 2026
Merged

feat: add a keyboard shortcuts help screen#294
stabldev merged 5 commits into
stabldev:mainfrom
YousefHadder:feat/help-screen

Conversation

@YousefHadder

@YousefHadder YousefHadder commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

This adds a ? overlay listing every binding, grouped by where it applies, since most keys only do something in either the search results or the downloads list. Closes the "Keyboard Shortcuts Overlay / Help Screen" roadmap item.

How it works

? needs priority=True to fire while a list has focus. But ? is also an ordinary character someone might type into a search query, so check_action() stands the binding down whenever an Input is focused. Because the binding stays live while the modal is open, action_show_help() toggles rather than guarding — a second ? closes it instead of doing nothing.

The panel scrolls with the same keys as the rest of the app (j/k, ctrl+d/ctrl+u, gg/G). A VerticalScroll only binds the arrow keys by default, which would have left this screen contradicting the vim-style keys it documents. The list already overflows a 24-row terminal today.

SHORTCUTS is a plain data list, so adding a binding later is one line, and the container is height: auto with max-height: 80% so it grows and scrolls on its own.

help-screen

Rebased on #292

This is now rebased onto main with #292 merged, and the overlap is resolved rather than pending.

#292 restructured the TUI controls docs into the same sections this screen uses, so docs/usage.md needed a real merge rather than a replay: the new rows drop into its "Anywhere in the app" table and the prose section sits alongside it, no duplicated tables. Its four new bindings are listed here too — s, S, f, x — with x described as "reset to your defaults" rather than "clear filters", since action_clear_filters resets to the configured default_sort / default_sort_order / min_seeders baseline, not to raw relevance.

Also added a Menus section covering the theme and sort selectors, which bind identically (j/k, enter, esc). Worth having because ? is reachable from inside those menus — check_action() only stands down for an Input, and a ListView isn't one — so pressing ? there now answers "what do I do here?" instead of showing a list that omits it.

The pre-existing inaccuracies I'd left alone (r and q don't exist, d/D undocumented, Enter opens a details panel first) were fixed by #292's rewrite, so they're gone from the merged docs.

Note for #293

#293 still overlaps in docs/usage.md and src/torrra/app.py. The app.py one is just two adjacent imports; the methods on both sides coexist fine.

Worth a look when it lands: it binds f on the downloads view (open file manager) while #292 binds f on the search view (toggle seeded-only). Those can't collide at runtime — they're on opposite sides of the ContentSwitcher, so only one is ever in the focus chain — but the flat tables in usage.md will end up listing f twice with contradictory descriptions unless the rows are scoped. This screen's sections are context-scoped, so it represents that distinction correctly. Happy to add its space / enter / left / right rows once it merges.

Testing

13 unit tests plus a snapshot; full suite is 163 passed, 5 snapshots. Docs build clean under sphinx-build -W.

The unit tests assert content, not layout — which is exactly how an earlier min-width let rows wrap and pushed the bottom two sections out of view while everything stayed green. The snapshot covers that. It's deliberately sized 90x50, larger than its siblings: the full list is now 40 rows and the panel is capped at 80% of the screen, so anything shorter clips the bottom sections and the snapshot would never catch a regression down there. Adding the #292 rows tripped exactly that — the old 90x40 size started cutting off Downloads and Menus — so there's a comment on the test to grow it if SHORTCUTS grows.

Known limitation

? doesn't open help on the welcome screen. Its only focusable widget is the search input, so check_action() always suppresses the binding and the ? goes into the query box instead. Help works everywhere once you've searched. Fixable, but the fix means special-casing an empty input, so I'd rather do it separately if we want it.

torrra had no way to discover its keybindings from inside the app. There is no
footer, and ENABLE_COMMAND_PALETTE is off, so Textual's built-in palette is
unavailable too. Learning that j/k navigate a list, or that p pauses a
download, meant leaving the app and reading the docs.

Press ? to open a modal listing every shortcut, grouped by where it applies,
using the same sections as the TUI controls tables in docs/usage.md. Press ?
again, or Esc, to close it.

The binding is priority so that it still fires while a list has focus.
That would otherwise swallow "?" while someone is typing a search query, so
check_action stands the binding down whenever an Input is focused. Because a
priority binding stays live while the modal is open, the action toggles rather
than guards, so a second ? closes the screen instead of doing nothing.

The panel is capped at 80% of the screen and scrolls with the same keys as the
rest of the app rather than arrows alone: VerticalScroll ships no vim
bindings, so the screen would otherwise list keys it then ignored.

Closes the "Keyboard Shortcuts Overlay / Help Screen" roadmap item.
@stabldev

Copy link
Copy Markdown
Owner

@YousefHadder LGTM! I made a few changes to the UI, though.
Give it a look and give me a 👍 if everything looks alright.

@stabldev
stabldev merged commit 9419986 into stabldev:main Aug 18, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants